#import "UITextView+BCPlaceHolder.h"
#import <objc/runtime.h>

@implementation UITextView (BCPlaceHolder)

#pragma mark - Swizzle Dealloc

+ (void)load
{
    // is this the best solution?
    method_exchangeImplementations(class_getInstanceMethod(self.class, NSSelectorFromString(@"dealloc")),
        class_getInstanceMethod(self.class, @selector(swizzledDealloc)));
}

- (void)swizzledDealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    UILabel* label = objc_getAssociatedObject(self, @selector(bc_placeholderLabel));
    if (label) {
        for (NSString* key in self.class.observingKeys) {
            @try {
                [self removeObserver:self forKeyPath:key];
            }
            @catch (NSException* exception) {
                // Do nothing
            }
        }
    }
    [self swizzledDealloc];
}

#pragma mark - Class Methods
#pragma mark `defaultPlaceholderColor`

+ (UIColor*)defaultPlaceholderColor
{
//    static UIColor* color = nil;
//    static dispatch_once_t onceToken;
//    dispatch_once(&onceToken, ^{
//        UITextField* textField = [[UITextField alloc] init];
//        textField.placeholder = @" ";
//        color = [textField valueForKeyPath:@"_placeholderLabel.textColor"];
//    });
//    return color;
    // Xcode 11 下使用 keyPath 会闪退，颜色暂时写死
    return [UIColor colorWithRed:.7 green:.7 blue:.7 alpha:1.0];
}

#pragma mark - `observingKeys`

+ (NSArray*)observingKeys
{
    return @[ @"attributedText",
        @"bounds",
        @"font",
        @"frame",
        @"text",
        @"textAlignment",
        @"textContainerInset" ];
}

#pragma mark - Properties
#pragma mark `placeholderLabel`

- (UILabel*)bc_placeholderLabel
{
    UILabel* label = objc_getAssociatedObject(self, @selector(bc_placeholderLabel));
    if (!label) {
        NSAttributedString* originalText = self.attributedText;
        self.text = @" "; // lazily set font of `UITextView`.
        self.attributedText = originalText;

        label = [[UILabel alloc] init];
        label.textColor = [self.class defaultPlaceholderColor];
        label.numberOfLines = 0;
        label.userInteractionEnabled = NO;
        objc_setAssociatedObject(self, @selector(bc_placeholderLabel), label, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

        self.needsUpdateFont = YES;
        [self updatePlaceholderLabel];
        self.needsUpdateFont = NO;

        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(updatePlaceholderLabel)
                                                     name:UITextViewTextDidChangeNotification
                                                   object:self];

        for (NSString* key in self.class.observingKeys) {
            [self addObserver:self forKeyPath:key options:NSKeyValueObservingOptionNew context:nil];
        }
    }
    return label;
}

#pragma mark `placeholder`

- (NSString*)bc_placeholder
{
    return self.bc_placeholderLabel.text;
}
- (void)setBc_placeholder:(NSString *)bc_placeholder {
    self.bc_placeholderLabel.text = bc_placeholder;
    [self updatePlaceholderLabel];
}


- (NSAttributedString*)bc_attributedPlaceholder
{
    return self.bc_placeholderLabel.attributedText;
}

- (void)setBc_attributedPlaceholder:(NSAttributedString*)bc_attributedPlaceholder
{
    self.bc_placeholderLabel.attributedText = bc_attributedPlaceholder;
    [self updatePlaceholderLabel];
}

#pragma mark `placeholderColor`

- (UIColor*)bc_placeholderColor
{
    return self.bc_placeholderLabel.textColor;
}

- (void)setBc_placeholderColor:(UIColor*)bc_placeholderColor
{
    self.bc_placeholderLabel.textColor = bc_placeholderColor;
}
- (UIFont*)bc_placeholderFont
{
    return self.bc_placeholderLabel.font;
}
- (void)setBc_placeholderFont:(UIFont *)bc_placeholderFont
{
    self.bc_placeholderLabel.font = bc_placeholderFont;
}
#pragma mark `needsUpdateFont`

- (BOOL)needsUpdateFont
{
    return [objc_getAssociatedObject(self, @selector(needsUpdateFont)) boolValue];
}

- (void)setNeedsUpdateFont:(BOOL)needsUpdate
{
    objc_setAssociatedObject(self, @selector(needsUpdateFont), @(needsUpdate), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

#pragma mark - KVO

- (void)observeValueForKeyPath:(NSString*)keyPath
                      ofObject:(id)object
                        change:(NSDictionary*)change
                       context:(void*)context
{
    if ([keyPath isEqualToString:@"font"]) {
        self.needsUpdateFont = (change[NSKeyValueChangeNewKey] != nil);
    }
    [self updatePlaceholderLabel];
}

#pragma mark - Update

- (void)updatePlaceholderLabel
{
    if (self.text.length) {
        [self.bc_placeholderLabel removeFromSuperview];
        return;
    }

    [self insertSubview:self.bc_placeholderLabel atIndex:0];

    if (self.needsUpdateFont) {
        self.bc_placeholderLabel.font = self.font;
        self.needsUpdateFont = NO;
    }
    self.bc_placeholderLabel.textAlignment = self.textAlignment;

    // `NSTextContainer` is available since iOS 7
    CGFloat lineFragmentPadding;
    UIEdgeInsets textContainerInset;

#pragma deploymate push "ignored-api-availability"
    // iOS 7+
    if (NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_6_1) {
        lineFragmentPadding = self.textContainer.lineFragmentPadding;
        textContainerInset = self.textContainerInset;
    }
#pragma deploymate pop

    // iOS 6
    else {
        lineFragmentPadding = 5;
        textContainerInset = UIEdgeInsetsMake(8, 0, 8, 0);
    }

    CGFloat x = lineFragmentPadding + textContainerInset.left;
    CGFloat y = textContainerInset.top;
    CGFloat width = CGRectGetWidth(self.bounds) - x - lineFragmentPadding - textContainerInset.right;
    CGFloat height = [self.bc_placeholderLabel sizeThatFits:CGSizeMake(width, 0)].height;
    self.bc_placeholderLabel.frame = CGRectMake(x, y, width, height);
}

@end
